package com.java.mvc;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @ClassName HandlerMapping
 * @Description 映射类
 * @Author 0715-YuHao
 * @Date 2020/9/7 17:39
 * @Version 1.0
 */
public class HandlerMapping {

    /**
     * @Author 0715-YuHao
     * @Description 创建集合,用于存储请求uri地址与映射对象一一对应关系
     * @Date 2020/9/9 11:05
     */
    private static  Map<String, MVCMapping> data = new HashMap<>();

    /**
     * @Author 0715-YuHao
     * @Description 获取映射对象
     * @Date 2020/9/9 10:58
     * @Param [uri] [请求uri地址]
     * @return com.java.mvc.HandlerMapping.MVCMapping
     */
    public static MVCMapping get(String uri) {
        return data.get(uri);
    }

    /**
     * @Author 0715-YuHao
     * @Description 映射器（包含大量的uri地址与方法的对应关系）
     * @Date 17:43 2020/9/7
     * @Param [is] 输入流获取本地配置文件
     * @return void
     **/
    public static void load(InputStream is) {
        //1. 加载本地配置文件
        Properties ppt = new Properties();
        try {
            ppt.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //2. 获取配置文件中描述的一个个的类
        Collection<Object> values = ppt.values();
        for (Object cla : values) {
            String className = String.valueOf(cla);
            try {
                //3. 加载配置文件中描述的每一个类
                Class c = Class.forName(className);
                //4. 创建这个类的对象
                Object obj = c.getConstructor().newInstance();
                //5. 获取这个类的所有方法
                Method[] methods = c.getMethods();
                for (Method m : methods) {
                    //6. 获取这个方法的所有注解
                    Annotation[] as = m.getAnnotations();
                    if (as != null) {
                        for (Annotation annotation : as) {
                            //7.判断该方法的注解类型
                            if (annotation instanceof ResponseBody) {
                                //说明此方法，用于返回字符串给客户端
                                //创建映射对象并将其与对应请求uri地址存入Map集合
                                MVCMapping mapping = new MVCMapping(obj, m, ResponseType.TEXT);
                                MVCMapping put = data.put(((ResponseBody) annotation).value(), mapping);
                                //存储失败，抛出异常请求地址重复
                                if (put != null) {
                                    throw new RuntimeException("请求地址重复:" + ((ResponseBody) annotation).value());
                                }
                            }else if (annotation instanceof ResponseView) {
                                //说明此方法，用于返回视图给客户端
                                MVCMapping mapping = new MVCMapping(obj, m, ResponseType.VIEW);
                                MVCMapping put = data.put(((ResponseView) annotation).value(), mapping);
                                if (put != null) {
                                    throw new RuntimeException("请求地址重复:" + ((ResponseView) annotation).value());
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @Author 0715-YuHao
     * @Description 用于存储控制器对象及其被注解的方法
     * @Date 17:44 2020/9/7
     * @Param
     * @return
     **/
    public static class MVCMapping {
        private Object obj;
        private Method method;
        private ResponseType type;

        public MVCMapping() {
        }

        public MVCMapping(Object obj, Method method, ResponseType type) {
            this.obj = obj;
            this.method = method;
            this.type = type;
        }

        public Object getObj() {
            return obj;
        }

        public void setObj(Object obj) {
            this.obj = obj;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public ResponseType getType() {
            return type;
        }

        public void setType(ResponseType type) {
            this.type = type;
        }
    }
}
